#pragma once

#include <functional>
#include <iostream>
#include <string>

#include "DLClassExpression.hpp"
#include "../Atom.hpp"

namespace terms {
	namespace dl {
		template <typename L=std::string, typename I=std::string, typename C=std::string, typename R=std::string>
		class DLClass
			: public virtual Atom,
			public virtual DLClassExpression<L, I, C, R>
		{	
		public:
			typedef DLClass<L, I, C, R> ThisClass;

		private:
			C _class;

		public: 
			DLClass(const C &cls)
				: _class(cls)
			{
			}

		public: 
			DLClass(ThisClass &&cls)
				: _class(std::move(cls._class))
			{
			}

		private:
			DLClass() = delete;

		public: 
			DLClass(const ThisClass &cls) = delete;


		public:
			virtual bool operator ==(const DLTerm<L, I, C, R> &term) const
			{
				if (this == &term) {
					return true;
				} else if (get_type_order() == term.get_type_order()) {
					const ThisClass *const other = dynamic_cast<const ThisClass *>(&term);
					if (other != nullptr) {
						return get_class() == other->get_class();
					}
				}	
				return false;
			}

		public:
			virtual bool operator <(const DLTerm<L, I, C, R> &term) const
			{
				if (get_type_order() < term.get_type_order()) {
					return true;
				} else if (get_type_order() == term.get_type_order()) {
					const ThisClass *other = dynamic_cast<const ThisClass *>(&term);
					return get_class() < other->get_class();
				} else {
					return false;
				}
			}

		public:
			virtual std::size_t
			hash() const
			{
				return std::hash<C>()(_class);
			}

		public:
			virtual std::string
			to_string() const
			{
				return std::to_string(_class);
			}
	
		public:
			virtual DLTermOrder
			get_type_order() const
			{
				return DLTermOrder::CLASS;
			}

		public:
			const C &
			get_class() const
			{
				return _class;
			}
		};
	}

}
